// Copyright 2021 Ben Wojtowicz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "SIB_Type.h"

#include <cmath>

int SIB_Type::Pack(std::vector<uint8_t> &bits)
{
    if(!present)
    {
        printf("SIB_Type::Pack() presence failure\n");
        return -1;
    }
    if(internal_value > k_spare1)
    {
        bits.push_back(1);
    }else{
        bits.push_back(0);
    }
    uint32_t N_bits = 4;
    if(internal_value > k_spare1)
        N_bits = 0;
    for(uint32_t i=0; i<N_bits; i++)
    {
        if(internal_value > k_spare1)
        {
            bits.push_back(((internal_value - k_spare1) >> (N_bits-1-i)) & 1);
        }else{
            bits.push_back((internal_value >> (N_bits-1-i)) & 1);
        }
    }
    return 0;
}

int SIB_Type::Unpack(std::vector<uint8_t> bits)
{
    uint32_t idx = 0;
    return Unpack(bits, idx);
}

int SIB_Type::Unpack(std::vector<uint8_t> bits, uint32_t &idx)
{
    if((idx + 1) > bits.size())
    {
        printf("SIB_Type::Unpack() index out of bounds for extension\n");
        return -1;
    }
    bool has_extension = bits[idx++];
    uint32_t packed_val = 0;
    uint32_t N_bits = 4;
    if(has_extension)
        N_bits = 0;
    if((idx + N_bits) > bits.size())
    {
        printf("SIB_Type::Unpack() index out of bounds for value\n");
        return -1;
    }
    for(uint32_t i=0; i<N_bits; i++)
        packed_val |= bits[idx++] << (N_bits-i-1);
    if(has_extension)
    {
        internal_value = (Enum)(packed_val + 16);
    }else{
        if(packed_val > 15)
        {
            printf("SIB_Type::Unpack() max failure\n");
            return -1;
        }
        internal_value = (Enum)packed_val;
    }
    present = true;
    return 0;
}

SIB_Type::Enum SIB_Type::Value() const
{
    if(present)
        return internal_value;
    return (SIB_Type::Enum)0;
}

int SIB_Type::SetValue(Enum value)
{
    internal_value = value;
    present = true;
    return 0;
}

int SIB_Type::SetValue(std::string value)
{
    if("sibType3" == value)
    {
        internal_value = k_sibType3;
        present = true;
        return 0;
    }
    if("sibType4" == value)
    {
        internal_value = k_sibType4;
        present = true;
        return 0;
    }
    if("sibType5" == value)
    {
        internal_value = k_sibType5;
        present = true;
        return 0;
    }
    if("sibType6" == value)
    {
        internal_value = k_sibType6;
        present = true;
        return 0;
    }
    if("sibType7" == value)
    {
        internal_value = k_sibType7;
        present = true;
        return 0;
    }
    if("sibType8" == value)
    {
        internal_value = k_sibType8;
        present = true;
        return 0;
    }
    if("sibType9" == value)
    {
        internal_value = k_sibType9;
        present = true;
        return 0;
    }
    if("sibType10" == value)
    {
        internal_value = k_sibType10;
        present = true;
        return 0;
    }
    if("sibType11" == value)
    {
        internal_value = k_sibType11;
        present = true;
        return 0;
    }
    if("sibType12_v920" == value)
    {
        internal_value = k_sibType12_v920;
        present = true;
        return 0;
    }
    if("sibType13_v920" == value)
    {
        internal_value = k_sibType13_v920;
        present = true;
        return 0;
    }
    if("spare5" == value)
    {
        internal_value = k_spare5;
        present = true;
        return 0;
    }
    if("spare4" == value)
    {
        internal_value = k_spare4;
        present = true;
        return 0;
    }
    if("spare3" == value)
    {
        internal_value = k_spare3;
        present = true;
        return 0;
    }
    if("spare2" == value)
    {
        internal_value = k_spare2;
        present = true;
        return 0;
    }
    if("spare1" == value)
    {
        internal_value = k_spare1;
        present = true;
        return 0;
    }
    return 1;
}

std::string SIB_Type::ValueToString(Enum value) const
{
    if(k_sibType3 == value)
        return "sibType3";
    if(k_sibType4 == value)
        return "sibType4";
    if(k_sibType5 == value)
        return "sibType5";
    if(k_sibType6 == value)
        return "sibType6";
    if(k_sibType7 == value)
        return "sibType7";
    if(k_sibType8 == value)
        return "sibType8";
    if(k_sibType9 == value)
        return "sibType9";
    if(k_sibType10 == value)
        return "sibType10";
    if(k_sibType11 == value)
        return "sibType11";
    if(k_sibType12_v920 == value)
        return "sibType12_v920";
    if(k_sibType13_v920 == value)
        return "sibType13_v920";
    if(k_spare5 == value)
        return "spare5";
    if(k_spare4 == value)
        return "spare4";
    if(k_spare3 == value)
        return "spare3";
    if(k_spare2 == value)
        return "spare2";
    if(k_spare1 == value)
        return "spare1";
    return "";
}

uint64_t SIB_Type::NumberOfValues() const
{
    return 16;
}

std::string SIB_Type::ToString(uint32_t indent) const
{
    std::string out;
    for(uint32_t i=0; i<indent; i++)
        out += "    ";
    out += "SIB_Type = " + ValueToString(internal_value) + "\n";
    return out;
}

std::string SIB_Type::ToStringNoNewLines() const
{
    std::string out = "SIB_Type=" + ValueToString(internal_value) + ",";
    return out;
}

int SIB_Type::Clear()
{
    present = false;
    return 0;
}

bool SIB_Type::IsPresent() const
{
    return present;
}

